package com.lightkits.wms.domain.pickreport.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.lightkits.common.CommonMethodsUtil;
import com.lightkits.common.MessageUtil;
import com.lightkits.common.TransactionUtil;
import com.lightkits.common.db.entity.FndEventTypeBase;
import com.lightkits.common.db.entity.FndWmMoveTypeBase;
import com.lightkits.common.db.entity.WmExecuteLine;
import com.lightkits.common.db.entity.WmRequestHeader;
import com.lightkits.common.db.entity.WmRequestLine;
import com.lightkits.common.db.entity.event.BdEvent;
import com.lightkits.common.db.entity.event.BdEventRecord;
import com.lightkits.common.model.CurrentWorkerInfo;
import com.lightkits.framework.common.exception.LightKitsBizException;
import com.lightkits.framework.common.model.CheckResult;
import com.lightkits.framework.common.model.IResult;
import com.lightkits.framework.common.utility.object.ObjectUtil;
import com.lightkits.framework.common.utility.object.ParseUtil;
import com.lightkits.framework.common.utility.object.ValueUtil;
import com.lightkits.framework.soa.client.id.GetGlobalIDRequestType;
import com.lightkits.framework.soa.client.id.IDServiceClient;
import com.lightkits.wms.domain.pickreport.ExecuteResModel;
import com.lightkits.wms.domain.pickreport.service.PickReportDomainService;
import com.lightkits.wms.domain.pickreport.service.PickReportTransactionService;
import com.lightkits.wms.dto.onhand.model.OnhandDTO;
import com.lightkits.wms.dto.pickreport.CancelPickReportRequestType;
import com.lightkits.wms.dto.pickreport.ExecutePickReportRequestType;
import com.lightkits.wms.dto.pickreport.models.QtyReqModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * PickReportDomainServiceImpl
 *
 * @author zhuangwei.zheng@hand-china.com
 * Created on 2019/5/8 15:56
 */
@Service
public class PickReportDomainServiceImpl implements PickReportDomainService {

    @Autowired
    private IDServiceClient idService;

    private final PickReportTransactionService pickReportTransactionService;

    @Autowired
    public PickReportDomainServiceImpl(PickReportTransactionService pickReportTransactionService) {
        this.pickReportTransactionService = pickReportTransactionService;
    }


    @Override
    public IResult checkParam(ExecutePickReportRequestType request) {
        List<QtyReqModel> reqModels = request.getReqModels();
        List<String> lineIds = new ArrayList<>();
        WmRequestHeader wmRequestHeader = new WmRequestHeader().selectById(request.getWmRequestId());
        if (wmRequestHeader == null) {
            return IResult.fail(MessageFormat
                    .format("WmRequestHeader not exist where id={0}", request.getWmRequestId()));
        }
        if (!"NEW".equals(wmRequestHeader.getProcessStatus()) && !"PRINTED".equals(wmRequestHeader.getProcessStatus())) {
            return IResult.fail(MessageUtil.getSysMessage("CHOOSE_NEW_OR_PRINTED_STATUS"));
        }
        reqModels.forEach(target -> lineIds.add(target.getLineId()));
        if (lineIds.isEmpty()) {
            return IResult.fail(MessageUtil.getSysMessage("CHANGE_ONE_PICK_REPORT"));
        }
        List<WmRequestLine> wmRequestLines = new WmRequestLine()
                .selectList(new EntityWrapper<WmRequestLine>()
                        .in(WmRequestLine.ID, lineIds));
        for (QtyReqModel reqModel : reqModels) {

            BigDecimal inputQty = ParseUtil.tryBigDecimal(reqModel.getCompleteQty());
            if (inputQty.compareTo(new BigDecimal(0)) < 0) {
                return IResult.fail(MessageUtil.getSysMessage("ILLEGAL_NUMBER"));
            }
            Optional<WmRequestLine> requestLineOpt = wmRequestLines
                    .stream()
                    .filter(target -> Objects.equals(target.getId().toString(), reqModel.getLineId()))
                    .findFirst();
            if (requestLineOpt.isPresent()) {
                WmRequestLine wmRequestLine = requestLineOpt.get();
                BigDecimal executeQty = inputQty.add(ValueUtil.getBigDecimal(wmRequestLine.getExeutedQty()));
                if (executeQty.compareTo(ValueUtil.getBigDecimal(wmRequestLine.getApplyQty())) > 0) {
                    return IResult.fail(MessageFormat.format(MessageUtil
                            .getSysMessage("OVERSTEP_EXECUTEQTY"), wmRequestLine.getLineNum()));
                }
            } else {
                return IResult.fail(MessageFormat
                        .format("WmRequestLine not exist where id = {0}", reqModel.getLineId()));
            }
        }
        return IResult.success();
    }

    @Override
    public CheckResult<ExecuteResModel> executePickReport(ExecutePickReportRequestType request) {
        Date now = new Date();
        CheckResult<ExecuteResModel> result = new CheckResult<>();
        result.setSuccessful(true);
        List<WmRequestHeader> requestHeaders = new ArrayList<>();
        List<WmExecuteLine> executeLines = new ArrayList<>();
        Long eventRequestId = idService.getGlobalId(new GetGlobalIDRequestType()).getId();
        CurrentWorkerInfo currentWorkerInfo = CommonMethodsUtil.getCurrentWorkerInfo(ParseUtil.tryLongWithException(request.getWorkerId()));
        WmRequestHeader requestHeader = new WmRequestHeader().selectById(request.getWmRequestId());

        FndWmMoveTypeBase moveTypeBase = new FndWmMoveTypeBase()
                .selectOne(new EntityWrapper<FndWmMoveTypeBase>()
                        .eq(FndWmMoveTypeBase.MOVE_TYPE_CODE, "WM_REQUEST_EXECUTE"));
        if (moveTypeBase == null) {
            return IResult.fail("moveTypeCode = WM_REQUEST_EXECUTE not exist.").fill(result);
        }
        FndEventTypeBase eventTypeBase = new FndEventTypeBase().selectById(moveTypeBase.getEventTypeId());
        if (eventTypeBase == null) {
            return IResult.fail(MessageFormat.format("eventType not exist where id = {0}",
                    moveTypeBase.getEventTypeId())).fill(result);
        }
        BdEvent event = createWmRequestExecuteEvent(requestHeader, eventRequestId,
                currentWorkerInfo, eventTypeBase, "");
        List<BdEvent> events = new ArrayList<>();
        events.add(event);
        List<String> lineIds = new ArrayList<>();
        Map<String, String> qtyValue = new HashMap<>();
        request.getReqModels().forEach(target -> {
            lineIds.add(target.getLineId());
            qtyValue.put(target.getLineId(), target.getCompleteQty());
        });
        List<WmRequestLine> requestLines = new WmRequestLine()
                .selectList(new EntityWrapper<WmRequestLine>()
                        .in(WmRequestLine.ID, lineIds)
                        .eq(WmRequestLine.REQUEST_ID, requestHeader.getId()));
        if (requestLines.isEmpty()) {
            return IResult.fail(MessageFormat.format("LineIds:{0}", String.join(",", lineIds))
                    .concat(";not of headId=".concat(requestHeader.getId().toString()))).fill(result);
        }
        List<BdEventRecord> eventRecords = new ArrayList<>();

        //更新领料单状态
        requestHeader.setProcessStatus("EXECUTED");
        BdEventRecord headStatusChangeRecord = new BdEventRecord();
        headStatusChangeRecord.setObjectType("WM_REQUEST_HEAD_ID");
        headStatusChangeRecord.setRecordType("DOCUMENT_STATUS");
        headStatusChangeRecord.setObjectId(requestHeader.getId());
        headStatusChangeRecord.setKeyValue(requestHeader.getProcessStatus());
        eventRecords.add(headStatusChangeRecord);

        requestLines.forEach(line -> {
            BigDecimal inputValue = ParseUtil.tryBigDecimal(qtyValue.get(line.getId().toString()));
            OnhandDTO onhandDTO = createOnHand(line, eventRequestId, currentWorkerInfo, inputValue);
            line.setExeutedQty(ValueUtil.getBigDecimal(line.getExeutedQty()).add(inputValue));
            line.setLineStatus("EXECUTED");
            onhandDTO.setOwnerType(line.getOwnerType());
            BdEventRecord record = new BdEventRecord();
            record.setObjectType("WM_REQUEST_LINE_ID");
            record.setObjectId(line.getId());
            record.setKeyValue(inputValue.toString());
            record.setRecordType("EXECUTED_QTY");
            eventRecords.add(record);
            WmExecuteLine executeLine = createWmExecuteLine(requestHeader,
                    line, currentWorkerInfo, inputValue, now);
            List<OnhandDTO> handDtoList = new ArrayList<>();
            handDtoList.add(onhandDTO);
            executeLine.store("OnhandDTO", handDtoList);
            executeLine.store(events);
            executeLines.add(executeLine);
        });
        requestHeader.store(requestLines);
        requestHeaders.add(requestHeader);
        event.store(eventRecords);
        result.setData(new ExecuteResModel() {
            {
                setEventRequestId(eventRequestId);
                setWmExecuteLines(executeLines);
                setWmRequestHeaders(requestHeaders);
            }
        });

        return result;
    }

    private OnhandDTO createOnHand(WmRequestLine line,
                                   Long eventRequestId,
                                   CurrentWorkerInfo workerInfo,
                                   BigDecimal inputQty) {
        OnhandDTO dto = new OnhandDTO();
        dto.setEventRequestId(eventRequestId);
        dto.setEventTypeCode("FP_ACCESS_STORAGE");
        dto.setWorkerId(workerInfo.getWorkerId().toString());
        dto.setOrgId(workerInfo.getDefaultOrganizationId().toString());
        dto.setItemId(ObjectUtil.toString(line.getItemId()));
        dto.setUomCode(line.getUom());
        dto.setWarehouseId(ObjectUtil.toString(line.getWarehouseId()));
        dto.setWmAreaId(ObjectUtil.toString(line.getWmAreaId()));
        dto.setChangeQuantity(ObjectUtil.toString(inputQty));
        dto.setChangeDirection("OUT");
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void startTransaction(ExecuteResModel data) {
        pickReportTransactionService.savePickReportHeads(data.getWmRequestHeaders());
        pickReportTransactionService.saveWmRequestExecute(data.getWmExecuteLines());
    }

    @Override
    public IResult checkCancelParam(CancelPickReportRequestType request) {
        WmRequestHeader requestHeader = new WmRequestHeader().selectById(request.getWmRequestId());
        if (requestHeader == null) {
            return IResult.fail(MessageFormat.format("WmRequestHeader not exist where id={0}",
                    request.getWmRequestId()));
        }
        if ("CANCELED".equals(requestHeader.getProcessStatus())) {
            return IResult.fail(MessageUtil.getSysMessage("PICK_REPORT_CANCELED"));
        }
        return IResult.success();
    }

    @Override
    public CheckResult<ExecuteResModel> cancelPickReport(CancelPickReportRequestType request) {
        CheckResult<ExecuteResModel> result = new CheckResult<>();
        result.setSuccessful(true);
        WmRequestHeader requestHeader = new WmRequestHeader().selectById(request.getWmRequestId());
        List<WmRequestLine> requestLines = new WmRequestLine()
                .selectList(new EntityWrapper<WmRequestLine>()
                        .eq(WmRequestLine.REQUEST_ID, requestHeader.getId()));
        requestHeader.setProcessStatus("CANCELED");
        requestLines.forEach(target -> target.setLineStatus("CANCELED"));
        requestHeader.store(requestLines);
        ExecuteResModel reportResModel = new ExecuteResModel();
        List<WmRequestHeader> headers = new ArrayList<>();
        headers.add(requestHeader);
        reportResModel.setWmRequestHeaders(headers);
        Long eventRequestId = idService.getGlobalId(new GetGlobalIDRequestType()).getId();
        BdEvent canceledEvent = createWmRequestExecuteEvent(requestHeader, eventRequestId,
                CommonMethodsUtil.getCurrentWorkerInfo(ParseUtil.tryLongWithException(request.getWorkerId())), null, "WM_REQUEST_CANCEL");
        BdEventRecord record = new BdEventRecord();
        record.setRecordType("DOCUMENT_STATUS");
        record.setObjectId(requestHeader.getId());
        record.setKeyValue(requestHeader.getProcessStatus());
        record.setObjectType("WM_REQUEST_HEAD_ID");
        List<BdEventRecord> records = new ArrayList<>();
        records.add(record);
        canceledEvent.store(records);
        List<BdEvent> events = new ArrayList<>();
        events.add(canceledEvent);
        reportResModel.setEvents(events);
        result.setData(reportResModel);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelTransaction(ExecuteResModel data) {
        pickReportTransactionService.savePickReportHeads(data.getWmRequestHeaders());
        new TransactionUtil().saveEvents(data.getEvents());
    }

    private WmExecuteLine createWmExecuteLine(WmRequestHeader requestHeader,
                                              WmRequestLine line,
                                              CurrentWorkerInfo workerInfo,
                                              BigDecimal inputQty,
                                              Date confirmTime) {
        WmExecuteLine executeLine = new WmExecuteLine();
        executeLine.setDocumentType("WM_REQUEST");
        executeLine.setDocumentId(requestHeader.getId());
        executeLine.setDocumentLineId(line.getId());
        executeLine.setExecuteType("CONFIRMED");
        executeLine.setItemId(line.getItemId());
        executeLine.setUom(line.getUom());
        executeLine.setExecutedQty(inputQty);
        executeLine.setExecutedTime(confirmTime);
        executeLine.setWarehouseId(line.getWarehouseId());
        executeLine.setWmAreaId(line.getWmAreaId());
        executeLine.setWorkcellId(workerInfo.getDefaultWkcId());
        executeLine.setToWarehouseId(line.getToWarehouseId());
        executeLine.setToWmAreaId(line.getToWmAreaId());
        return executeLine;
    }

    private BdEvent createWmRequestExecuteEvent(WmRequestHeader header,
                                                Long eventRequestId, CurrentWorkerInfo workerInfo,
                                                FndEventTypeBase eventTypeBase, String eventTypeCode) {
        BdEvent event = new BdEvent();
        event.setEventRequestId(eventRequestId);
        event.setWorkerId(workerInfo.getWorkerId());
        event.setOrganizationId(workerInfo.getDefaultOrganizationId());
        event.setProdLineId(header.getProdLineId());
        event.setWorkcellId(workerInfo.getDefaultWkcId());
        event.setCalendarDay(new Date());
        if (eventTypeBase == null) {
            eventTypeBase = new FndEventTypeBase()
                    .selectOne(new EntityWrapper<FndEventTypeBase>()
                            .eq(FndEventTypeBase.EVENT_TYPE_CODE, eventTypeCode));
            if (eventTypeBase == null) {
                throw new LightKitsBizException(MessageFormat.format("eventTypeCode = {0} not exist.", eventTypeCode));
            }
        }
        event.setEventTypeId(eventTypeBase.getId());
        event.setEventTypeCode(eventTypeBase.getEventTypeCode());
        return event;
    }
}
